<?php
	// Trước đó cần có lớp Fx_Data_View, Fx_Data_Column, Fx_Data_Column_Renderer, Fx_Data_Row
	class Fx_Data_Grid extends Fx_Data_Row
	{	
		// Collection Array(st_model)
		protected $_collection = array();
		// Tiêu đề các cột
		protected $_rowHeader = null;
		// Tập hợp các dòng Array(fx_data_row)
		protected $_rowCollection = array();
		// Chỉ mục hiện tại, nếu bảng không có bản ghi 
		// hoặc chưa lựa chọn dòng thì chỉ mục là -1
		protected $_currentIndex = -1;
		// Dòng hiện tại
		protected $_currentRow = null;
		// Công cụ trợ giúp việc sắp xếp và lọc bản ghi
		protected $_dataRowHelper = null;
		// row collection dùng để lấy lại trạng thái ban đầu khi chưa được sắp xếp hoặc lọc.
		protected $_originalRowCollection = null;
		// Cột được sắp xếp theo chiều nào
		protected $_sorting = array('field'=>'', 'sortOrder' => 'ASC');
		// Có cho phép lọc hay không
		protected $_allowFilter = true;
		// Url để sắp xếp theo cột
		protected $_sortUrl = '';
		// Url để filter theo cột
		protected $_filterUrl = '';
		
		// Hàm khởi tạo
		function __construct(&$_collection = array())
		{
			parent::__construct();
			if (count($_collection) > 0) 
			{
				$this->setCollection($_collection);	
			}
		}
		// Khởi tạo Bộ công cụ trợ giúp việc sắp xếp trước khi
		// khởi tạo đối tượng grid
		public function __afterInit()
		{
			if ($this->_dataRowHelper === null) {
				$this->_dataRowHelper = & Fx_Data_Row_Helper::getInstance();
			}
			$this->addClass('gridViewData');
			$this->addClass('fullWidth');
			return $this;
		}
		
		// Kiểm tra lại giá trị ID trước khi xuất ra html
		public function __beforeToHtml()
		{
			if (empty($this->_id))
			{
				$this->_id = 'defaultGridViewData';
			}
			$this->_id = 'defaultGridViewData';
			return $this;
		}
		
		// Sau khi xuất ra màn hình thì lưu lại trạng thái của gridview
		public function __afterToHtml()
		{
			$this->saveToRegistry($this->getId());
			return $this;
		}

		// Thiết lập sắp xếp theo url
		public function setSortUrl($url)
		{
			if (!is_string($url))
			{
				return false;	
			}
			$this->_sortUrl = $url;
			return $this;
		}
		
		// Lấy về url để thực thi sắp xếp
		public function getSortUrl()
		{
			return $this->_sortUrl;
		}
		
		// Thiết lập lọc theo url
		public function setFilterUrl($url)
		{
			if (!is_string($url))
			{
				return false;	
			}
			$this->_filterUrl = $url;
			return $this;
		}
		
		// Lấy về url để thực hiện lọc theo cột
		public function getFilterUrl()
		{
			return $this->_filterUrl;
		}
		
		// Thiết lập các dòng bản ghi (hiếm khi sử dụng trực tiếp)
		// Các dòng bản ghi sẽ tự động được cập nhật khi cập nhật
		// tập hợp bản ghi collection
		public function setRowCollection($_collection = array())
		{
			if (is_array($_collection) && count($_collection) > 0)	
			{
				$this->_rowCollection = $_collection;	
			}
			$_rsCollected = array();
			foreach ($this->_rowCollection as $_row)
			{
				$_entity = $_row->getModel();
				$_rsCollected[$_entity->getId()] = & $_entity;
			}
			if ($_rsCollected > 0)
			{
				$this->_collection = & $_rsCollected;	
			} 
			else 
			{
				$this->setCollection($this->_collection);	
			}
			return $this;
		}
		
		// Có cho phép lọc hay không
		public function allowFilter($answer = true)
		{
			$this->_allowFilter = $answer;
			return $this;
		}
		
		// Thiết lập chỉ mục hiện tại
		public function setSelectedIndex($index = 0)
		{
			if (!is_int($index)) {
				$index = intval($index);	
			}
			if ($index < -1) {
				$index = -1;
			}	
			// Thiết lập chỉ mục hiện thời của bảng
			$this->_currentIndex = $index;
			// Thiết lập luôn dòng hiện tại của bảng
			if ($index >= 0)
			{
				$this->_currentRow = & $this->_rowCollection[$index];
			}
			return $this;
		}
		
		// Lấy về chỉ ục hiện tại đang được chọn
		public function getSelectedIndex()
		{
			return (int)$this->_currentIndex;	
		}
		
		// Thiết lập dòng hiện tại
		public function setSelectedRow($row)
		{
			$this->_currentRow = & $row;	
			return $this;
		}
		
		// Lấy về dòng hiện tại
		public function getSelectedRow()
		{
			return $this->_currentRow;
		}
		
		// Lấy về tiêu đề cột
		public function & getRowHeader()
		{
			return $this->_rowHeader;
		}
		
		// Thiết lập tiêu đề cột
		public function setRowHeader($_row = null)
		{
			if ($_row != null)	
			{
				if ($this->_rowHeader === null) {
					$this->_rowHeader = $_row;	
				}
			}
			return $this;
		}
		
		// Lấy về tập hợp các dòng bản ghi
		public function getRowCollection()
		{
			return (array)$this->_rowCollection;
		}
		
		// Thiết lập tập hợp các bản ghi, đồng thời thêm vào các
		// dòng bản ghi tương ứng
		public function setCollection($_collection = array())
		{
			if (is_array($_collection) && count($_collection) > 0)	
			{
				$this->_collection = & $_collection;
				$this->_rowCollection = array();
				foreach ($_collection as $_model) 
				{
					$this->addRow($_model);
				}
				if ($this->_originalRowCollection === null)
				{
					$this->_originalRowCollection = $this->_rowCollection;	
				}
			}
			return $this;
		}
		
		// Lấy về tập hợp các bản ghi
		public function getCollection()
		{
			return (array)$this->_collection;
		}
		
		// Thêm một dòng mới
		public function addRow($_entity = null)
		{
			$_model = null;
			if ($_entity instanceof st_model) 
			{
				$_model = & $_entity;
			} else if ($_entity instanceof Fx_Data_Row)
			{
				$_model = & $_entity->getModel();	
			}
			if ($_model != null && $_model->isLoaded())
			{
				// Quy tắc khóa của tập hợp sẽ không chứa id của row mà chỉ chứa chỉ mục
				$_row = new Fx_Data_Row($_model);
				$_row->setDataGridView($this);
				$this->_rowCollection[$_row->getId()] = $_row;
				$_row->setIndex(count($this->_rowCollection)-1);
				$this->autoSetRowHeader();
			}
			return $this;
		}
		
		
		// Xóa dòng theo chỉ mục
		public function removeRow($index)
		{
			if ($this->_rowCollection == 0)
			{
				return $this;	
			}
			$currentIndex = 0;
			foreach ($this->_rowCollection as $row)
			{
				if ($currentIndex === $index)
				{
					unset($this->_rowCollection[$row->getId()]);
					break;
				}
				$currentIndex++;
			}
			return $this;
		}
		
		// Lấy về một dòng theo chỉ mục
		public function & getDataRow($index)
		{
			if (count($this->_rowCollection) == 0)
			{
				return null;	
			}
			if (isset($this->_rowCollection[$index])) 
			{
				
				return $this->_rowCollection[$index]; 
			}
			return null;
		}
		
		// Lấy về một dòng theo ID
		public function & getDataRowById($id)
		{
			if (count($this->_rowCollection) == 0)
			{
				return null;	
			}
			foreach ($this->_rowCollection as $_row) 
			{
				if ($_row->getId() == $id)
				{
					return $_row;	
				}
			}
			return null;
		}
		
		// Tự động thiết lập header cho bảng
		public function autoSetRowHeader()
		{
			if ($this->_rowHeader === null) {
				$this->setRowHeader(end($this->_rowCollection));	
			}
			return $this;
		}
		
		// Lấy về hộp công cụ thực hiện các thao tác đồng loạt
		public function getMassActionRow()
		{
			$output	= <<<OUTPUT
			<table cellspacing="0" cellpadding="0" class="massaction">
				<tr>
					<td>
						<a onclick="" href="#">
							Select All
						</a>
						<span class="separator">|</span>
						<a onclick="" href="#">
							Unselect All
						</a>
						<span class="separator">|</span>
						<a onclick="" href="#">
							Select Visible
						</a>
						<span class="separator">|</span>
						<a onclick="" href="#">
							Unselect Visible
						</a>
						<span class="separator">|</span>
						<strong id="">120</strong>
						items selected
					</td>
					<td>
					</td>
				</tr>
			</table>
OUTPUT;
			return $output;
		}
		
		// Nếu cho phép lọc thì hiển thị bộ lọc
		public function getFilterRow()
		{
			$headerRow = & $this->getRowHeader();
			$output	= '<tr class="filterRow">';
			foreach ($headerRow->getColumnCollection() as $column)
			{
				if ($column->isFilterable())
				{
					$output	.= $column->getFilter()->getOutputHtml();	
				}
				else
				{
					$output	.= '<td class="columnFilter"></td>';	
				}
			}
			$output	.= '</tr>';
			return $output;
		}

		// Lấy về nội dung html
		public function getOutputHtml()
		{	
			$output = '';
			if ($this->_allowFilter) {
				if (empty($this->_filterUrl))
				{
					throw new Exception('You must set filter action url for grid view id: ' . $this->_id);				
				}
				$filterUrl = base_url() . $this->_filterUrl . '/';
				$output .= "<form action=\"$this->_filterUrl\" method=\"post\" id=\"frmGridId_$this->_id\" class=\"gridViewForm\" onsubmit=\"return filterBy('$this->_id', '$filterUrl', false)\">";
				$output .= "<input type=\"submit\" value=\" Search \"/>";
				$output .= "<input type=\"reset\" value=\" Reset Filter \" id=\"resetButton\" onclick=\"return filterBy('$this->_id', '$filterUrl', true)\"/>";
			}
			$output	.= $this->getMassActionRow();
			$output .= "<table class=\"gridViewData fullWidth\" id=\"$this->_id\" cellpadding=\"0\" cellspacing=\"0\">\n";
			$output .= $this->_rowHeader->getOutputHeader();
			if ($this->_allowFilter)
			{
				$output	.= $this->getFilterRow();
			}
			$index = 0;
			foreach ($this->_rowCollection as $_row)
			{
				$_row->setIndex($index);
				$output .= $_row->getOutputHtml();
				$index++;
			}
			$output .= "</table>\n";
			if ($this->_allowFilter) {
				$output .= "</form>\n";
			}
			return $output;	
		}
		
		// Sắp xếp row theo field
		public function sortBy($field, $sorting = 'ASC')
		{
			$_collection = & $this->_rowCollection;
			$this->_dataRowHelper->sortRowCollection($_collection, $field, $sorting);
			$this->setRowCollection($_collection);
			$this->_sorting['field'] = $field;
			$this->_sorting['sortOrder'] = $sorting;
			return $this;
		}
		
		// Sắp xếp row theo field (sắp xếp trực tiếp trên collection)
		public function sortCollection($field, $sorting = 'ASC')
		{
			$_collection = & $this->_collection;
			$this->_dataRowHelper->sortByField($_collection, $field, $sorting);
			$this->setCollection($_collection);
			$this->_sorting['field'] = $field;
			$this->_sorting['sortOrder'] = $sorting;
			return $this;
		}
		
		// Sắp xếp các bản ghi theo điều kiện
		// Cách dùng exp: username:equal:anhdt
		public function filterBy($exp)
		{
			if (!is_string($exp))
			{
				return $this;	
			}
			$_conditions = explode(' and ', $exp);
			foreach ($_conditions as $cond) {
				$_elements = explode(':', $cond);
				if (count($_elements) != 3)
				{
					return $this;	
				}
				$_collection = & $this->_rowCollection;
				$this->_dataRowHelper->filterRowCollection($_collection, $_elements[0], $_elements[1], $_elements[2]);
				$this->setRowCollection($_collection);
			}
			return $this;
		}
		
		// Sắp xếp các bản ghi theo điều kiện
		// Cách dùng exp: username:equal:anhdt
		public function filterCollection($exp)
		{
			if (!is_string($exp))
			{
				return $this;	
			}
			$_conditions = explode(' and ', $exp);
			foreach ($_conditions as $cond) 
			{
				$_elements = explode(':', $cond);
				if (count($_elements) != 3)
				{
					return $this;	
				}
				$_collection = & $this->_collection;
				$this->_dataRowHelper->filterByField($_collection, $_elements[0], $_elements[1], $_elements[2]);
				$this->setCollection($_collection);
			}
			return $this;
		}
		
		// Reset lại phiên bản đầu tiên của tập hợp các bản ghi
		public function resetRowCollection()
		{
			$this->setRowCollection($this->_originalRowCollection);
			return $this;	
		}
		
		// Thêm cột mới cho table
		// Lấy về các cột đã tồn tại và thêm cột mới
		public function addColumn(Fx_Data_Column $_column)
		{
			foreach ($this->_rowCollection as $_row)
			{
				$_row->addColumn($_column);
			}
			return $this;
		}
		
		// Kiểm tra có tồn tại cột
		public function hasColumn($_column)
		{
			$_rowHeader = & $this->getRowHeader();
			return ($_rowHeader->hasColumn($_column));
		}
		
		// Lấy về một column
		public function & getColumn($_column)
		{
			$_rowHeader = & $this->getRowHeader();
			$_column = & $_rowHeader->getColumnById($_column);
			return $_column;
		}
	}
?>